'use client'
import { createRoot } from "react-dom/client";
import { isValidElement } from "react";

import type { ReactElement, RefObject } from "react";

let current: null | (() => void) = null;

type Params = {
  type: string;
  msg: string | number | ReactElement;
  duration: number;
  maskClickable: boolean;
};
type OmitParams = Omit<Params, "type"> | string | number | ReactElement;

const create = (params: Params, ref?: RefObject<HTMLElement>): (() => void) => {
  const { msg, type, duration, maskClickable } = params;
  if (typeof current === "function") {
    current();
  }
  let timer: number;
  const dom: HTMLElement = ref?.current || document.body;
  const container: HTMLDivElement = document.createElement("div");
  dom.appendChild(container);
  const root = createRoot(container);
  const close: () => void = (): void => {
    root?.unmount?.();
    dom.contains(container) && dom.removeChild(container);
    current = null;
    clearTimeout(timer);
  };
  current = close;
  root.render(
    <div
      style={{
        position: "fixed",
        inset: 0,
        pointerEvents: maskClickable ? "none" : "auto",
        display: type === "center" ? "flex" : undefined,
        alignItems: type === "center" ? "center" : undefined,
        justifyContent: type === "center" ? "center" : undefined,
        zIndex: 9999
      }}
    >
      <style>
        {`
        @keyframes fadeIn {
          from {
            opacity: 0;
          }
          to {
            opacity: 1;
          }
        }
        @keyframes rotate {
          0% {
            transform: rotate(0deg);
          }
          100% {
            transform: rotate(360deg);
          }
        }
      `}
      </style>
      <div
        style={{
          pointerEvents: "none",
          width: "90%",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          position: "absolute",
          left: "50%",
          top: type === "top" ? 64 : undefined,
          bottom: type === "bottom" ? 64 : undefined,
          transform: "translateX(-50%)",
        }}
      >
        <div
          style={{
            pointerEvents: "auto",
            wordBreak: "break-all",
            background: "rgba(0,0,0,.7)",
            borderRadius: 8,
            padding: 12,
            color: "white",
            animation: "fadeIn 0.2s ease-in-out",
            fontSize: 16,
          }}
        >
          {msg}
        </div>
      </div>
    </div>
  );
  if (typeof duration === "number" && duration > 0) {
    timer = window.setTimeout(close, duration * 1000);
  }
  return close;
};

function filterParams(params: OmitParams): Omit<Params, "type"> {
  let newParams: Omit<Params, "type"> = {
    msg: "",
    duration: 3,
    maskClickable: true,
  };
  if (
    isValidElement(params) ||
    typeof params === "number" ||
    typeof params === "string"
  ) {
    newParams.msg = params;
  } else {
    newParams = {
      ...newParams,
      ...params,
    };
  }
  return newParams;
}

export class Toast {
  static top(params: OmitParams, ref?: RefObject<HTMLElement>): () => void {
    return create({ ...filterParams(params), type: "top" }, ref);
  }
  static center(params: OmitParams, ref?: RefObject<HTMLElement>): () => void {
    return create({ ...filterParams(params), type: "center" }, ref);
  }
  static bottom(params: OmitParams, ref?: RefObject<HTMLElement>): () => void {
    return create({ ...filterParams(params), type: "bottom" }, ref);
  }
  static loading(params: OmitParams, ref?: RefObject<HTMLElement>): () => void {
    const newParams = filterParams(params);
    if (
      typeof params === "number" ||
      typeof params === "string" ||
      isValidElement(params)
    ) {
      newParams.duration = 0;
      newParams.maskClickable = false;
      newParams.msg = "loading...";
    }
    const Svg = (
      <div
        style={{
          padding: "16px 20px",
          display: "flex",
          flexDirection: "column",
          justifyItems: "center",
          alignItems: "center",
        }}
      >
        <svg
          width="42"
          height="42"
          style={{
            animation: "rotate .8s linear infinite",
          }}
        >
          <circle
            cx="50%"
            cy="50%"
            r="20"
            stroke="white"
            strokeWidth="2"
            fill="none"
            strokeLinecap="square"
            strokeDasharray="62.83185307179586"
          ></circle>
        </svg>
        <div
          style={{
            marginTop: 10,
            fontSize: 14,
          }}
        >
          {newParams.msg}
        </div>
      </div>
    );
    return create({ ...newParams, msg: Svg, type: "center" }, ref);
  }
}
